package org.wutopia.labcloud.domain.product.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.wutopia.labcloud.domain.product.controller.vo.input.ProductCreateProductInputVO;
import org.wutopia.labcloud.domain.product.controller.vo.input.ProductModifyProductInputVO;
import org.wutopia.labcloud.domain.product.controller.vo.output.*;
import org.wutopia.labcloud.domain.product.dao.ProductMainDao;
import org.wutopia.labcloud.domain.product.dao.ProductSpecificationDao;
import org.wutopia.labcloud.domain.product.dao.ProductStockpileDao;
import org.wutopia.labcloud.domain.product.dao.entity.BaseEntity;
import org.wutopia.labcloud.domain.product.dao.entity.mysql.ProductMain;
import org.wutopia.labcloud.domain.product.dao.entity.mysql.ProductSpecification;
import org.wutopia.labcloud.domain.product.dao.entity.mysql.ProductStockpile;
import org.wutopia.labcloud.domain.product.service.ProductService;
import org.wutopia.labcloud.library.common.base.PageInputVO;
import org.wutopia.labcloud.library.common.base.PageOutputVO;
import org.wutopia.labcloud.service.api.service.dubbo.FileService;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@CacheConfig(cacheNames = "resource.product")
@Slf4j
public class ProductServiceImpl implements ProductService {

    private ProductMainDao productMainDao;

    private ProductSpecificationDao productSpecificationDao;

    private ProductStockpileDao productStockpileDao;

    private ElasticsearchClient elasticsearchClient;

    @DubboReference
    private FileService fileService;


    @Override
    @Cacheable(key = "#id")
    public ProductGetProducetOutputVO getProductById(Long id) {


        ProductMain productMain = productMainDao.selectById(id);


        QueryWrapper<ProductStockpile> q = new QueryWrapper<>();
        q.eq("product_id", id);
        ProductStockpile productStockpile = productStockpileDao.selectOne(q);


        QueryWrapper<ProductSpecification> q2 = new QueryWrapper<>();
        q2.eq("product_id", id);
        List<ProductSpecification> productSpecifications = productSpecificationDao.selectList(q2);

        List<ProductGetSpecificationOut> specList = new ArrayList<>();

        for(ProductSpecification spec : productSpecifications) {
            ProductGetSpecificationOut specOut = new ProductGetSpecificationOut();
            specOut.setId(spec.getId());
            specOut.setItem(spec.getItem());
            specOut.setValue(spec.getValue());
            specList.add(specOut);
        }

        ProductGetProducetOutputVO vo = new ProductGetProducetOutputVO();
        vo.setId(productMain.getId());
        vo.setCreateTime(productMain.getCreateTime());
        vo.setUpdateTime(productMain.getUpdateTime());
        vo.setTitle(productMain.getTitle());
        vo.setDescription(productMain.getDescription());
        vo.setPrice(productMain.getPrice());
        vo.setRate(productMain.getRate());
        vo.setCoverAddr(productMain.getCoverAddr());
        vo.setDetailAddr(productMain.getDetailAddr());

        vo.setStockpile(productStockpile.getAmount());

        vo.setSpecificationList(specList);


        return vo;
    }

    @Override
    @Transactional
    public ProductCreateProductOutputVO createProduct(ProductCreateProductInputVO vo) {
        ProductMain productMain = new ProductMain();

        productMain.setRate(vo.getRate());
        productMain.setTitle(vo.getTitle());
        productMain.setPrice(vo.getPrice());
        productMain.setDescription(vo.getDescription());
        productMain.setDetailAddr(vo.getDetailAddr());
        productMain.setCoverAddr(vo.getCoverAddr());

        productMainDao.insert(productMain);


        ProductStockpile productStockpile = new ProductStockpile();

        productStockpile.setProductId(productMain.getId());
        productStockpile.setAmount(vo.getStockpile());
        productStockpile.setFrozen(0);
        productStockpileDao.insert(productStockpile);

        List<ProductGetSpecificationOut> specificationList = vo.getSpecificationList();

        List<ProductSpecification> collect = specificationList.stream()  // 创建流
                .map(out -> {
                    ProductSpecification spec = new ProductSpecification();
                    spec.setProductId(productMain.getId());
                    spec.setItem(out.getItem());    // 复制 item 字段
                    spec.setValue(out.getValue());  // 复制 value 字段
                    return spec;
                })
                .collect(Collectors.toList());
        productSpecificationDao.insert(collect);


        ProductCreateProductOutputVO outVO = new ProductCreateProductOutputVO();
        outVO.setId(productMain.getId());
        outVO.setCreateTime(productMain.getCreateTime());
        outVO.setUpdateTime(productMain.getUpdateTime());
        outVO.setTitle(productMain.getTitle());
        outVO.setDescription(productMain.getDescription());
        outVO.setPrice(productMain.getPrice());
        outVO.setRate(productMain.getRate());
        outVO.setCoverAddr(productMain.getCoverAddr());
        outVO.setDetailAddr(productMain.getDetailAddr());

        outVO.setStockpile(productStockpile.getAmount());

        outVO.setSpecificationList(vo.getSpecificationList());

        return outVO;
    }

    @Override
    @Transactional
    public void modify(ProductModifyProductInputVO vo) {

        ProductMain product = productMainDao.selectById(vo.getId());

        ProductMain productMain = new ProductMain();

        productMain.setId(vo.getId());
        productMain.setRate(vo.getRate());
        productMain.setTitle(vo.getTitle());
        productMain.setPrice(vo.getPrice());
        productMain.setDescription(vo.getDescription());


        if(vo.getDetailAddr() != null && !vo.getDetailAddr().equals(product.getDetailAddr())) {
            String[] split1 = product.getDetailAddr().split(":");
            if(split1.length == 2 && !split1[1].equals("Default.jpg")) {
                fileService.deleteFile(split1[0], split1[1]);
            }
        }

        if(vo.getCoverAddr() != null && !vo.getCoverAddr().equals(product.getCoverAddr())) {
            String[] split = product.getCoverAddr().split(":");
            if(split.length == 2 && !split[1].equals("Default.jpg")) {
                fileService.deleteFile(split[0], split[1]);
            }
        }
        productMain.setDetailAddr(vo.getDetailAddr());
        productMain.setCoverAddr(vo.getCoverAddr());

        productMainDao.updateById(productMain);


        UpdateWrapper<ProductStockpile> wrapper = new UpdateWrapper<>();
        wrapper.eq("product_id", vo.getId())
                .set("amount", vo.getStockpile());

        productStockpileDao.update(wrapper);


        // 获取传入 规格 list
        List<ProductGetSpecificationOut> specificationList = vo.getSpecificationList();

        // 转化为 new entity list
        List<ProductSpecification> newList = specificationList.stream()  // 创建流
                .map(out -> {
                    ProductSpecification spec = new ProductSpecification();
                    if(out.getId() != null) {
                        spec.setId(out.getId());
                    }
                    spec.setItem(out.getItem());    // 复制 item 字段
                    spec.setValue(out.getValue());  // 复制 value 字段
                    return spec;
                })
                .toList();

        Set<Long> newListIds = newList.stream().map(( spec) -> spec.getId()).collect(Collectors.toSet());

        // 获取 old entity list
        QueryWrapper<ProductSpecification> q2 = new QueryWrapper<>();
        q2.eq("product_id", vo.getId());
        List<ProductSpecification> oldList = productSpecificationDao.selectList(q2);

        Set<Long> oldListIds = oldList.stream().map(( spec) -> spec.getId()).collect(Collectors.toSet());
        log.info("newListIds: {}, oldListIds: {}", newListIds, oldListIds);
        log.info("newList: {}, oldList: {}", newList, oldList);
        /**
         * exsample:
         * newList
         * [
         *  {id: 1,item: 作者,value: wu},{id: 2,item: s,value: wuf},{id: null,item: sg,value: wzuf}
         * ]
         *
         * oldList
         * [
         *  {id: 1,item: 作者,value: wu},{id: 2,item: s,value: wuf},{id: 3,item: sagg,value: wzsdfuf}
         * ]
         *
         * insertList
         * [{id: null,item: sg,value: wzuf}]
         *
         * deleteList
         * [{id: 3,item: sagg,value: wzsdfuf}]
         */


        // 获取 newList 有, 但 oldList 没有的元素 既是新增的 规格
        List<ProductSpecification> insertList = newList.stream()
                .filter(item -> {
                    if(!oldListIds.contains(item.getId())) {
                        item.setProductId(vo.getId());

                        return true;
                    }
                    return false;
                })
                .collect(Collectors.toList());

        log.info("新增规格: {}", insertList);
        productSpecificationDao.insert(insertList);


        // 获取 oldList 有, 但 newList 没有的元素 既是删除的 规格
        List<ProductSpecification> deletetList = oldList.stream()
                .filter(item -> !newListIds.contains(item.getId()))
                .toList();
        log.info("删除规格: {}", deletetList);
        deletetList.forEach(item -> {
            productSpecificationDao.deleteById(item.getId());
        });

    }

    @Override
    @GlobalTransactional
    public void deleteById(Long id) {

        ProductMain productMain = productMainDao.selectById(id);

        String[] split = productMain.getCoverAddr().split(":");
        if(split.length == 2 && !split[1].equals("Default.jpg")) {
            fileService.deleteFile(split[0], split[1]);
        }

        String[] split1 = productMain.getDetailAddr().split(":");
        if(split1.length == 2 && !split1[1].equals("Default.jpg")) {
            fileService.deleteFile(split1[0], split1[1]);
        }

        productMainDao.deleteById(id);

        QueryWrapper<ProductStockpile> q = new QueryWrapper<>();
        q.eq("product_id", id);
        productStockpileDao.delete(q);

        QueryWrapper<ProductSpecification> q2 = new QueryWrapper<>();
        q2.eq("product_id", id);
        productSpecificationDao.delete(q2);
    }

    @Override
    public PageOutputVO<List<ProductGetListOutputVO>> getList(PageInputVO vo) throws IOException {


//        Integer from = (vo.getPage() - 1) * vo.getSize();

//        SearchResponse<Product> response =
//            elasticsearchClient.search(s -> s
//                    .index("products")
//                    .query(q -> q
//                            .match(t -> t
//                                    .field("name")
//                                    .query(vo.getKeyword())
//                            ))
//                    .from(from)
//                    .size(vo.getSize())
//            ,
//            Product.class
//        );

        Page<ProductMain> productMainPage = productMainDao.selectPage(new Page<>(vo.getPage(), vo.getSize()), new QueryWrapper<>());

        List<ProductMain> records = productMainPage.getRecords();



        List<ProductGetListOutputVO> list = new ArrayList<>();

        for(ProductMain productMain : records) {
            ProductGetListOutputVO out = new ProductGetListOutputVO();
            QueryWrapper<ProductStockpile> q = new QueryWrapper<>();
            q.eq("product_id", productMain.getId());
            ProductStockpile productStockpile = productStockpileDao.selectOne(q);


            QueryWrapper<ProductSpecification> q2 = new QueryWrapper<>();
            q2.eq("product_id", productMain.getId());
            List<ProductSpecification> productSpecifications = productSpecificationDao.selectList(q2);

            List<ProductGetSpecificationOut> specList = new ArrayList<>();
            for(ProductSpecification spec : productSpecifications) {
                ProductGetSpecificationOut specOut = new ProductGetSpecificationOut();
                specOut.setId(spec.getId());
                specOut.setItem(spec.getItem());
                specOut.setValue(spec.getValue());
                specList.add(specOut);
            }

            out.setCreateTime(productMain.getCreateTime());
            out.setUpdateTime(productMain.getUpdateTime());
            out.setId(productMain.getId());
            out.setTitle(productMain.getTitle());

            out.setCoverAddr(productMain.getCoverAddr());

            out.setPrice(productMain.getPrice());
            out.setDescription(productMain.getDescription());
            out.setRate(productMain.getRate());

            out.setDetailAddr(productMain.getDetailAddr());

            out.setStockpile(productStockpile.getAmount());
            out.setSpecificationList(specList);
            list.add(out);
        }

        PageOutputVO<List<ProductGetListOutputVO>> outVO = new PageOutputVO<>();

        outVO.setTotal(productMainPage.getTotal());
        outVO.setSize(productMainPage.getSize());
        outVO.setPage(productMainPage.getCurrent());
        outVO.setTotalPage(productMainPage.getPages());
        outVO.setData(list);

        return outVO;
    }

}
